﻿using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Data.Schema.Package.Project;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Data.Schema.Package.Internal;
using System.Collections.Generic;
using System.IO;
using Microsoft.VisualStudio.Shell;
using EnvDTE;

namespace TFDP.VisualStudioInterop
{
    public class VsInteropUtilities
    {
        public static Guid VS2010SqlDatabaseProject = new Guid("{c8d11400-126e-41cd-887f-60bd40844f9e}");

        /// <summary>
        /// Obtain the filename of the given IVsHierarchy
        /// </summary>
        /// <param name="hierarchy">The IVsHierarchy to examine</param>
        /// <returns>The filename or String.Empty if the filename could not be obtained</returns>
        public static string GetFileNameFromHierarchy(IVsHierarchy hierarchy)
        {
            string fileName = String.Empty;

            uint itemId = GetItemId(hierarchy);

            hierarchy.GetCanonicalName(itemId, out fileName);

            return fileName;
        }

        /// <summary>
        /// Obtain the item id of the IVsHierarchy
        /// </summary>
        /// <param name="hierarchy"></param>
        /// <returns></returns>
        public static uint GetItemId(IVsHierarchy hierarchy)
        {
            object extObject;
            uint itemId = 0;
            IVsHierarchy tempHierarchy;

            hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_BrowseObject, out extObject);

            IVsBrowseObject browseObject = extObject as IVsBrowseObject;
            if (browseObject != null)
                browseObject.GetProjectItem(out tempHierarchy, out itemId);

            return itemId;
        }

        /// <summary>
        /// Obtain the IVsHierarchy specified by the document cookie from the RunningDocumentTable
        /// </summary>
        /// <param name="table">The IVsRunningDocumentTable to search</param>
        /// <param name="cookie">The document cookie</param>
        /// <returns>A IVsHierarchy that points to the document</returns>
        public static IVsHierarchy GetHierarchyFromDocCookie(IVsRunningDocumentTable table, uint cookie)
        {
            uint pgrfRDTFlags;
            uint pdwReadLocks;
            uint pdwEditLocks;
            string pbstrMkDocument;
            IVsHierarchy hierarchy;
            uint pitemid;
            IntPtr ppunkDocData;

            table.GetDocumentInfo(
                cookie,
                out pgrfRDTFlags,
                out pdwReadLocks,
                out pdwEditLocks,
                out pbstrMkDocument,
                out hierarchy,
                out pitemid,
                out ppunkDocData
            );

            return hierarchy;
        }

        /// <summary>
        /// Obtain the EnvDTE.Project of a given IVsHierarchy
        /// </summary>
        /// <param name="hierarchy">The IVsHierarchy to examine</param>
        /// <returns>A EnvDTE.Project instance that is the parent of the IVsHierarchy</returns>
        public static EnvDTE.Project GetEnvDTEProject(IVsHierarchy hierarchy)
        {
            object prjObject = null;

            hierarchy.GetProperty((uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_ExtObject, out prjObject);

            return (EnvDTE.Project)prjObject;
        }

        public static IEnumerable<IVsProject> ProjectsInSolution(IVsSolution solution)
        {
            IEnumHierarchies enumerator = null;
            Guid guid = Guid.Empty;
            solution.GetProjectEnum((uint)__VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION, ref guid, out enumerator);
            IVsHierarchy[] hierarchy = new IVsHierarchy[1] { null };
            uint fetched = 0;
            for (enumerator.Reset(); enumerator.Next(1, hierarchy, out fetched) == VSConstants.S_OK && fetched == 1; /*nothing*/)
            {
                yield return (IVsProject)hierarchy[0];
            }
        }

        public static IEnumerable<string> GetProjectsInSolution()
        {
            IVsSolution solutionService = Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(IVsSolution)) as IVsSolution;

            if (solutionService != null)
            {
                var projects = VsInteropUtilities.ProjectsInSolution(solutionService);
                return projects
                    .Select(p => VsInteropUtilities.GetEnvDTEProject((IVsHierarchy)p))
                    .Select(p => p.Name)
                    .ToList();
            }

            return new List<string>();
        }

        public static IEnumerable<string> GetFilesInDirectory(
            string projectName,
            string relativePath,
            Func<string, bool> filter)
        {
            if (String.IsNullOrEmpty(projectName))
            {
                throw new ArgumentNullException("projectName");
            }

            IVsSolution solutionService = Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(IVsSolution)) as IVsSolution;
            EnvDTE.Project project = null;

            if (solutionService == null)
            {
                return new List<string>();
            }

            var projects = VsInteropUtilities.ProjectsInSolution(solutionService);
            project = projects
                .Select(p => VsInteropUtilities.GetEnvDTEProject((IVsHierarchy)p))
                .Where(p => p.Name == projectName)
                .FirstOrDefault();

            if (project == null)
            {
                return new List<string>();
            }

            EnvDTE.ProjectItem folder = VsInteropUtilities.GetFolderProjectItem(project, relativePath);

            if (folder == null)
            {
                return new List<string>();
            }

            return folder
                .ProjectItems
                .OfType<EnvDTE.ProjectItem>()
                .Where(pi => filter(pi.Name))
                .Select(pi => pi.Name)
                .ToList();
        }

        public static string GetActiveProject()
        {
            DTE dte = Package.GetGlobalService(typeof(SDTE)) as DTE;

            Array activeSolutionProjects = dte.ActiveSolutionProjects as Array;

            EnvDTE.Project activeProject = null;

            if (activeSolutionProjects != null && activeSolutionProjects.Length > 0)
            {
                activeProject = activeSolutionProjects.GetValue(0) as Project;
            }

            return activeProject.Name;
        }

        private static EnvDTE.ProjectItem GetFolderProjectItem(EnvDTE.Project project, string relativePath)
        {
            string[] pathParts = relativePath.Split(
                new char[] { Path.DirectorySeparatorChar },
                StringSplitOptions.RemoveEmptyEntries
            );

            // Check if the target folders exist, starting at the root of the project
            EnvDTE.ProjectItem targetFolder = project.ProjectItems.Item(pathParts[0]) as EnvDTE.ProjectItem;

            // Index of ProjectItems starts at 1 not 0
            for (int i = 1; i < pathParts.Length; i++)
            {
                targetFolder = targetFolder.ProjectItems.Item(pathParts[i]);

                if (targetFolder == null)
                {
                    return null;
                }
            }

            return targetFolder;
        }
    }
}
